
#ifndef PROTOCOLMODBUS_HPP
#define PROTOCOLMODBUS_HPP
#include <stdint.h>
#include <stdbool.h>
#include "protocol.hpp"
#include "cppbase.h"
#include "firmwaredownload.hpp"

using namespace ota;

#define PROTOCOLMODBUS_DATAMAX_SURPPORT							256
#define PROTOCOLMODBUS_MULTWRITE_REGISTERS_SURPPORT	20	///<写寄存器数量限值
#define PROTOCOLMODBUS_WRITE_REGISTERLEN_SURPPORT		24	///<写寄存器长度,最小不低于4

namespace protocols
{
	class protocolmodbus:public protocol
	{
		public:
			typedef enum
			{
				MODBUS_FUNCTION_READCOIL = 1,							///<读线圈
				MODBUS_FUNCTION_READINPUT = 2,						///<读输入线圈
				MODBUS_FUNCTION_READREG = 3,							///<读寄存器
				MODBUS_FUNCTION_READINPUTREG = 4,					///<读输入寄存器
				MODBUS_FUNCTION_WRITECOIL = 5,						///<写线圈
				MODBUS_FUNCTION_WRITEREG = 6,							///<写寄存器
				MODBUS_FUNCTION_WRITEMULTREG = 0x10,			///<写多个寄存器
				MODBUS_FUNCTION_SLAVEREPORT = 0x11,				///<报告从站
			}ModbusFunctionTPDF;												///<功能码
			
			typedef enum
			{
				REGISTER_READONLY = 0,										///<只读
				REGISTER_WRITEONLY,												///<只写
				REGISTER_READWRITE,												///<读写
			}RegisterReadWriteTPDF;											///<寄存器读写权限
			
			typedef struct
			{
				bool bLimitEnable;																///<上下限检查使能
				const VariableAttributeTPDF tVariableAttribute;		///<变量属性
				/** 
				* @brief      	自定义寄存器上下限判断回调
				* @param[in]  	usRegisterAddr   	寄存器地址
				* @param[in]  	ucData   					数据
				* @param[in]  	usLen							数据长度
				* @retval     	操作结果(true:数据检查成功)
				*/
				bool (*CustomRegisterLimitCallBack)(uint16_t usRegisterAddr, uint8_t *ucData, uint16_t usLen);
			}RegisterAttributeTPDF;															///<寄存器属性
			
			typedef struct
			{
				int32_t iKey;																								///<键值
				uint8_t ucData[PROTOCOLMODBUS_WRITE_REGISTERLEN_SURPPORT];	///<数据
				uint16_t usLen;																							///<数据长度
				uint16_t usResourceOffset;																	///<数据源地址偏移量
			}WriteDataAttributeTPDF;																			///<写数据数据属性
			
			/** 
			* @brief      	特殊写指令委托方法
			* @param[in]  	ucSlaveAddr   从机地址
			* @param[in]  	tCommand   		16位指令
			* @param[in]  	ucData   			数据
			* @param[in]  	usLen   			数据长度
			* @retval     	操作结果(返回false后,协议返回错误3报文)
			*/
			typedef bool (*SpeOrderDelegate)(uint8_t ucSlaveAddr, uint16_t tCommand, uint8_t *ucData, uint16_t usLen);
			
			/** 
			* @brief      	写指令委托方法
			* @param[in]  	tWriteDataAttribute   	写数据数据属性
			* @param[in]  	usLen   								寄存器总数
			* @retval     	操作结果(返回false后,协议返回错误4报文)
			*/
			typedef bool (*WriteOrderDelegate)(WriteDataAttributeTPDF *tWriteDataAttribute, uint16_t usLen);
			
			/** 
			* @brief      	广播委托方法
			* @param[in]  	tprotocol   	协议
			* @retval     	操作结果(返回false后,不响应主机)
			*/
			typedef bool (*BroadCastDelegate)(protocolmodbus *tprotocol);
			
			/** 
			* @brief      	报告从站
			* @param[out]  	ucData   	数据
			* @retval     	数据长度
			*/
			typedef uint8_t (*RequestReportDataDelegate)(uint8_t *ucData);
			
			typedef struct
			{
				RegisterReadWriteTPDF RegisterReadWrite;					///<读写权限
				ModbusFunctionTPDF Function;											///<功能码
				uint16_t RegisterStartAddr;												///<寄存器起始地址
				uint16_t ResourceLen;															///<数据源长度
				uint8_t *Resource;																///<数据源
				const RegisterAttributeTPDF *tRegisterAttribute;	///<寄存器属性
				uint16_t usRegisterAttributeLen;									///<属性长度
				WriteOrderDelegate WriteOrderCallBack;						///<写回调
			}ModbusRegPartitionTPDF;														///<分区表
			
		protected:
			uint16_t mModbusRegPartitionsLen;									///<分区表长度
			uint16_t usWriteDataAttributeLen;									///<写数据属性长度
			ModbusRegPartitionTPDF *mModbusRegPartitions;			///<分区表
			/** 写数据*/
			WriteDataAttributeTPDF mWriteDataAttribute[PROTOCOLMODBUS_MULTWRITE_REGISTERS_SURPPORT];
			
			
			/** 
			* @brief      	特殊写指令内部处理
			* @param[in]  	ucSlaveAddr   从机地址
			* @param[in]  	tCommand   		16位指令
			* @param[in]  	ucData   			数据
			* @param[in]  	usLen   			数据长度
			* @retval     	操作结果(返回modbus错误代码,0表示正确响应,-1表示不响应)
			*/
			virtual int8_t BaseSpeOrderProcess(uint8_t ucSlaveAddr, uint16_t tCommand, uint8_t *ucData, uint16_t usLen);
		
			/** 
			* @brief      	拷贝数据
			* @param[in]  	tModbusRegPartitions   分区表
			* @param[out]  	ucTag   		目标数据
			* @retval     	None
			*/
			virtual void DataCopy(ModbusRegPartitionTPDF *tModbusRegPartitions, uint8_t *ucTag);
		
			/** 
			* @brief      	检查数据
			* @param[in]  	tModbusRegPartitions   分区表
			* @retval     	None
			*/
			virtual bool DataCheckAndWriteDataParse(ModbusRegPartitionTPDF *tModbusRegPartitions);
			
		public:
			uint8_t SlaveAddr;																		///<从站地址
			uint8_t BroadCastAddr;																///<广播地址
			ModbusFunctionTPDF Function;													///<功能码
			uint8_t ErrorCode;																		///<错误代码
			uint16_t RegisterAddr;																///<寄存器地址
			uint16_t DataLen;																			///<数据长度
			uint8_t Data[PROTOCOLMODBUS_DATAMAX_SURPPORT];				///<数据
			SpeOrderDelegate SpeOrderCallBack;										///<特殊写指令回调
			RequestReportDataDelegate RequestReportDataCallBack;	///<报告从站回调
			BroadCastDelegate BroadCastCallBack;									///<广播回调
		
			/** 
			* @brief      	构造函数
			* @retval     	None
			*/
			protocolmodbus(void);
		
			/** 
			* @brief      	析构函数
			* @retval     	None
			*/
			virtual ~protocolmodbus(void);
			
			/** 
			* @brief      	封包
			* @param[in]  	ucData   		数据
			* @retval     	数据长度
			*/
			virtual uint16_t Pack(uint8_t *ucData);
			
			/** 
			* @brief      	拆包
			* @param[in]  	ucData   		数据
			* @param[out]  	usDataLen   数据长度
			* @retval     	None
			*/
			virtual bool Unpack(uint8_t *ucData, uint16_t usDataLen);
			
			/** 
			* @brief      	注册分区表
			* @param[in]  	tModbusRegPartitions   		分区表
			* @param[out]  	usModbusRegPartitionsLen  分区表长度
			* @retval     	None
			*/
			virtual void RegistePartitionTable(ModbusRegPartitionTPDF *tModbusRegPartitions, uint16_t usModbusRegPartitionsLen);
			
			/** 
			* @brief      	分区表映射操作
			* @param[in]  	tOutput   			输出协议
			* @param[out]  	ucSlaveAddr   	从站地址
			* @retval     	None
			*/
			virtual bool Map(protocolmodbus *tOutput, uint8_t ucSlaveAddr);
	};
}
#endif
